﻿// ===============================================================================
// OpenSessionInViewModule.cs
// EOS Web Framework
// ===============================================================================
// Copyright (c) EOS Development Group. All rights reserved.
// ===============================================================================

namespace EOS.Framework.Hibernate
{
    using System;
    using System.Web;
    using EOS.Framework.Kernel;
    using Microsoft.Practices.Unity;
    using NHibernate;
    using NHibernate.Cfg;

    /// <summary>
    /// A http module responsible, that the nhibernate session is always available during the web session.
    /// </summary>
    public class HibernateSessionModule : IHttpModule
    {
        private static ISessionFactory _sessionFactory;

        private const string KeyNHibernateFactory = "NHibernateSessionFactory";
        private const string KeyNHibernateSession = "NHibernateSession";

        static HibernateSessionModule()
        {
            InitSessionFactory();
        }

        private static void InitSessionFactory()
        {
            _sessionFactory = new Configuration().Configure().BuildSessionFactory();

            if (_sessionFactory == null)
            {
                throw new InvalidOperationException("Call to Configuration.BuildSessionFactory() returned null.");
            }
        }

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="HibernateSessionModule"/> is reclaimed by garbage collection.
        /// </summary>
        ~HibernateSessionModule()
        {
            Dispose(false);
        }

        #region IHttpModule Members

        /// <summary>
        /// Initializes a module and prepares it to handle requests.
        /// </summary>
        /// <param name="context">An <see cref="T:System.Web.HttpApplication"/> that provides 
        /// access to the methods, properties, and events common to all application objects within an ASP.NET application</param>
        public void Init(HttpApplication context)
        {
            context.BeginRequest += new EventHandler(OnBeginTransaction);
            context.EndRequest += new EventHandler(OnEndTransaction);
        }

        /// <summary>
        /// Called when when the transaction is started.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnBeginTransaction(object sender, EventArgs e)
        {
            HttpApplication application = (HttpApplication)sender;
            HttpContext context = application.Context;

            Unity.Current.RegisterInstance<ISession>(CurrentSession, new RequestLifetimeManager<ISession>());
        }

        /// <summary>
        /// Called when the transaction is finished.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void OnEndTransaction(object sender, EventArgs e)
        {
            HttpApplication application = (HttpApplication)sender;
            HttpContext context = application.Context;

            ISession session = context.Items[KeyNHibernateSession] as ISession;

            if (session != null)
            {
                try
                {
                    session.Flush();
                }
                catch
                {
                    session.Close();
                }
            }
        }

        #endregion

        public static ISessionFactory CurrentFactory
        {
            get
            {
                return _sessionFactory;
            }
        }

        public static ISession CreateSession()
        {
            ISessionFactory factory = CurrentFactory;

            if (factory == null)
            {
                throw new InvalidOperationException("Call to Configuration.BuildSessionFactory() returned null.");
            }

            ISession session = factory.OpenSession();

            if (session == null)
            {
                throw new InvalidOperationException("Call to factory.OpenSession() returned null.");
            }


            return session;
        }

        public static ISession CurrentSession
        {
            get
            {
                HttpContext context = HttpContext.Current;

                ISession session = context.Items[KeyNHibernateSession] as ISession;
                if (session == null)
                {
                    session = CreateSession();
                }

                return session;
            }
        } 

        #region IDipose Members

        /// <summary>
        /// Disposes of the resources (other than memory) used by the 
        /// module that implements <see cref="T:System.Web.IHttpModule"/>.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and 
        /// unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing)
        {
        }

        #endregion
    }
}
